package client;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.ArrayList;


import decode.DecodeMessage;

import file.BufferMap;
import file.File;

import utils.Utils;


public class PeerConnection implements Runnable{

	private Socket _socket;
	private Thread _thread;
	private Client _client;

	public PeerConnection(Socket s, Client client){
		_socket = s;
		_thread = new Thread(this, "Server connected to client");
		_client = client;
		_thread.start();
	}

	@Override
	public void run() {
		try {
			DecodeMessage._client = _client;
			String read = DecodeMessage.decodeMessageFromSocket(_socket.getInputStream(), "clientServer");
			if(read != null){
				if(read.matches("^interested [a-f0-9]+")){
					if(this.respondToInterested(read)){
						read = DecodeMessage.decodeMessageFromSocket(_socket.getInputStream(), "clientServer");
						if(read == null){
							System.out.println("Socket closed !");
							this.releaseConnection();
						}
						else{
							
							if(read.matches("^getpieces [a-f0-9]+ \\[([0-9]+( )*)+\\]")){
								this.respondToGetPieces(read);
							}
						}
					}
					else{
						this.releaseConnection();
					}
				}
				else if(read.matches("^have [a-f0-9]+ .*")){
					if(!this.respondToHave(read)){
						
						this.releaseConnection();
					}
				}
			}
			this.releaseConnection();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void releaseConnection(){
		try {
			_socket.close();
			_client._SEMAPHORE_NB_CONNECTION_.release();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @return the _socket
	 */
	public final Socket get_socket() {
		return _socket;
	}

	private boolean respondToInterested(String read) throws UnsupportedEncodingException{
		String[] split = read.split(" ");
		String hash = split[1];
		int pos = 0, i = 0;
		boolean found = false;
		ArrayList<File> tmp = new ArrayList<File>();
		tmp.addAll(_client.getMySeed());
		tmp.addAll(_client.getMyLeech());
		while(i < tmp.size() && !found){
			if(tmp.get(i).getIdFile().equals(hash)){
				found = true;
				pos = i;
			}
			i++;
		}
		if(found){
			try{
			String bufferMap = new String(tmp.get(pos).getBufferMap().toByteArray(), "ISO-8859-1");
			String message = "have " + hash + " " + bufferMap;
			System.out.println(message);
				_socket.getOutputStream().write(message.getBytes("ISO-8859-1"));
			} catch (UnsupportedEncodingException e1) {
				e1.printStackTrace();
			}catch (IOException e){
				e.printStackTrace();
				return false;
			}
			return true;
		}
		else{
			return false;
		}
	}

	private void respondToGetPieces(String read){
		String[] split = read.split(" ");
		String hash = split[1];
		
		read = read.replaceAll("\\]", "");
		split = read.split("\\[");
		String[] askedParts = split[1].split(" ");

		int pos = 0, i = 0;
		boolean found = false;
		boolean isSeed = false;
		ArrayList<File> tmp = new ArrayList<File>();
		tmp.addAll(_client.getMySeed());
		tmp.addAll(_client.getMyLeech());
		while(i < tmp.size() && !found){
			if(tmp.get(i).getIdFile().equals(hash)){
				found = true;
				isSeed = tmp.get(i).getBufferMap().isComplete();
				pos = i;
			}
			i++;
		}
		if(found){
			long offsetDelta = (isSeed) ? 0 : 1;
			long pieceSize = tmp.get(pos).getPieceSize();
			java.io.File file = new java.io.File(tmp.get(pos).getPath());

			RandomAccessFile f;
			try {
				f = new RandomAccessFile(file, "r");

				byte[] data = new byte[(int) pieceSize];
				String toSend = "data " + hash + " [";
				String space;
				String dataByte;
				for(i = 0; i < askedParts.length; i++){
					space = (i == askedParts.length - 1 ) ? "" : " ";
					f.seek(Integer.parseInt(askedParts[i])*pieceSize + (Integer.parseInt(askedParts[i]) + 1)*offsetDelta);
					
					f.read(data);
					dataByte = new String(data, "ISO-8859-1");
					
					toSend += askedParts[i]+":"+dataByte+space;
				}
				toSend += "]";
				
				_socket.getOutputStream().write(toSend.getBytes("ISO-8859-1"));				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	private boolean respondToHave(String read){
		String[] split = read.split(" ");
		String hash = split[1];
		int pos = 0, i = 0;
		boolean found = false;
		ArrayList<File> tmp = new ArrayList<File>();
		tmp.addAll(_client.getMySeed());
		tmp.addAll(_client.getMyLeech());
		while(i < tmp.size() && !found){
			if(tmp.get(i).getIdFile().equals(hash)){
				found = true;
				pos = i;
			}
			i++;
		}
		if(found){
			BufferMap bm = new BufferMap(tmp.get(pos).getBufferMap().getLength());
			bm.fillBufferMap(split[2].getBytes());
			tmp.get(pos).setDispo(bm);
			String bufferMap = new String(tmp.get(pos).getBufferMap().toByteArray());
			String message = "have " + hash + " " + bufferMap;
			
			try {
				_socket.getOutputStream().write(message.getBytes());
			} catch (IOException e){
				e.printStackTrace();
				return false;
			}
			return true;
		}
		else{
			return false;
		}
	}
}
